﻿#include "MLibrary.h"
#include "../Settings.h"
#include "../../Shared/Enums.h"
#include "DXManager.h"
#include "../Forms/Client.CMain.h"

using namespace SlimDX;
using namespace SlimDX::Direct3D9;
using Frame = Client::MirObjects::Frame;
using namespace Client::MirObjects;

namespace Client::MirGraphics
{

bool Libraries::Loaded = false;
int Libraries::Count = 0;
int Libraries::Progress = 0;
MLibrary *const Libraries::ChrSel = new MLibrary(Settings::DataPath + "ChrSel");
MLibrary *const Libraries::Prguse = new MLibrary(Settings::DataPath + "Prguse");
MLibrary *const Libraries::Prguse2 = new MLibrary(Settings::DataPath + "Prguse2");
MLibrary *const Libraries::Prguse3 = new MLibrary(Settings::DataPath + "Prguse3");
MLibrary *const Libraries::BuffIcon = new MLibrary(Settings::DataPath + "BuffIcon");
MLibrary *const Libraries::Help = new MLibrary(Settings::DataPath + "Help");
MLibrary *const Libraries::MiniMap = new MLibrary(Settings::DataPath + "MMap");
MLibrary *const Libraries::Title = new MLibrary(Settings::DataPath + "Title");
MLibrary *const Libraries::MagIcon = new MLibrary(Settings::DataPath + "MagIcon");
MLibrary *const Libraries::MagIcon2 = new MLibrary(Settings::DataPath + "MagIcon2");
MLibrary *const Libraries::Magic = new MLibrary(Settings::DataPath + "Magic");
MLibrary *const Libraries::Magic2 = new MLibrary(Settings::DataPath + "Magic2");
MLibrary *const Libraries::Magic3 = new MLibrary(Settings::DataPath + "Magic3");
MLibrary *const Libraries::Effect = new MLibrary(Settings::DataPath + "Effect");
MLibrary *const Libraries::MagicC = new MLibrary(Settings::DataPath + "MagicC");
MLibrary *const Libraries::GuildSkill = new MLibrary(Settings::DataPath + "GuildSkill");
MLibrary *const Libraries::Background = new MLibrary(Settings::DataPath + "Background");
MLibrary *const Libraries::Dragon = new MLibrary(Settings::DataPath + "Dragon");
const std::vector<MLibrary*> Libraries::MapLibs = std::vector<MLibrary*>(400);
MLibrary *const Libraries::Items = new MLibrary(Settings::DataPath + "Items");
MLibrary *const Libraries::StateItems = new MLibrary(Settings::DataPath + "StateItem");
MLibrary *const Libraries::FloorItems = new MLibrary(Settings::DataPath + "DNItems");
MLibrary *const Libraries::Deco = new MLibrary(Settings::DataPath + "Deco");
std::vector<MLibrary*> Libraries::CArmours;
std::vector<MLibrary*> Libraries::CWeapons;
std::vector<MLibrary*> Libraries::CWeaponEffect;
std::vector<MLibrary*> Libraries::CHair;
std::vector<MLibrary*> Libraries::CHumEffect;
std::vector<MLibrary*> Libraries::AArmours;
std::vector<MLibrary*> Libraries::AWeaponsL;
std::vector<MLibrary*> Libraries::AWeaponsR;
std::vector<MLibrary*> Libraries::AHair;
std::vector<MLibrary*> Libraries::AHumEffect;
std::vector<MLibrary*> Libraries::ARArmours;
std::vector<MLibrary*> Libraries::ARWeapons;
std::vector<MLibrary*> Libraries::ARWeaponsS;
std::vector<MLibrary*> Libraries::ARHair;
std::vector<MLibrary*> Libraries::ARHumEffect;
std::vector<MLibrary*> Libraries::Monsters;
std::vector<MLibrary*> Libraries::Gates;
std::vector<MLibrary*> Libraries::Flags;
std::vector<MLibrary*> Libraries::Mounts;
std::vector<MLibrary*> Libraries::NPCs;
std::vector<MLibrary*> Libraries::Fishing;
std::vector<MLibrary*> Libraries::Pets;
std::vector<MLibrary*> Libraries::Transform;
std::vector<MLibrary*> Libraries::TransformMounts;
std::vector<MLibrary*> Libraries::TransformEffect;
std::vector<MLibrary*> Libraries::TransformWeaponEffect;

    Libraries::StaticConstructor::StaticConstructor()
    {
        //Wiz/War/Tao
        InitLibrary(CArmours, Settings::CArmourPath, "00");
        InitLibrary(CHair, Settings::CHairPath, "00");
        InitLibrary(CWeapons, Settings::CWeaponPath, "00");
        InitLibrary(CWeaponEffect, Settings::CWeaponEffectPath, "00");
        InitLibrary(CHumEffect, Settings::CHumEffectPath, "00");
        
        //Assassin
        InitLibrary(AArmours, Settings::AArmourPath, "00");
        InitLibrary(AHair, Settings::AHairPath, "00");
        InitLibrary(AWeaponsL, Settings::AWeaponPath, "00", " L");
        InitLibrary(AWeaponsR, Settings::AWeaponPath, "00", " R");
        InitLibrary(AHumEffect, Settings::AHumEffectPath, "00");
        
        //Archer
        InitLibrary(ARArmours, Settings::ARArmourPath, "00");
        InitLibrary(ARHair, Settings::ARHairPath, "00");
        InitLibrary(ARWeapons, Settings::ARWeaponPath, "00");
        InitLibrary(ARWeaponsS, Settings::ARWeaponPath, "00", " S");
        InitLibrary(ARHumEffect, Settings::ARHumEffectPath, "00");
        
        //Other
        InitLibrary(Monsters, Settings::MonsterPath, "000");
        InitLibrary(Gates, Settings::GatePath, "00");
        InitLibrary(NPCs, Settings::NPCPath, "00");
        InitLibrary(Mounts, Settings::MountPath, "00");
        InitLibrary(Fishing, Settings::FishingPath, "00");
        InitLibrary(Pets, Settings::PetsPath, "00");
        InitLibrary(Transform, Settings::TransformPath, "00");
        InitLibrary(TransformMounts, Settings::TransformMountsPath, "00");
        InitLibrary(TransformEffect, Settings::TransformEffectPath, "00");
        InitLibrary(TransformWeaponEffect, Settings::TransformWeaponEffectPath, "00");
        
        #pragma region Maplibs
        //wemade mir2 (allowed from 0-99)
        MapLibs[0] = new MLibrary(Settings::DataPath + "Map\\WemadeMir2\\Tiles");
        MapLibs[1] = new MLibrary(Settings::DataPath + "Map\\WemadeMir2\\Smtiles");
        MapLibs[2] = new MLibrary(Settings::DataPath + "Map\\WemadeMir2\\Objects");
        for (int i = 2; i < 27; i++)
        {
            MapLibs[i + 1] = new MLibrary(Settings::DataPath + "Map\\WemadeMir2\\Objects" + std::to_string(i));
        }
        //shanda mir2 (allowed from 100-199)
        MapLibs[100] = new MLibrary(Settings::DataPath + "Map\\ShandaMir2\\Tiles");
        for (int i = 1; i < 10; i++)
        {
            MapLibs[100 + i] = new MLibrary(Settings::DataPath + "Map\\ShandaMir2\\Tiles" + std::to_string(i + 1));
        }
        MapLibs[110] = new MLibrary(Settings::DataPath + "Map\\ShandaMir2\\SmTiles");
        for (int i = 1; i < 10; i++)
        {
            MapLibs[110 + i] = new MLibrary(Settings::DataPath + "Map\\ShandaMir2\\SmTiles" + std::to_string(i + 1));
        }
        MapLibs[120] = new MLibrary(Settings::DataPath + "Map\\ShandaMir2\\Objects");
        for (int i = 1; i < 31; i++)
        {
            MapLibs[120 + i] = new MLibrary(Settings::DataPath + "Map\\ShandaMir2\\Objects" + std::to_string(i + 1));
        }
        MapLibs[190] = new MLibrary(Settings::DataPath + "Map\\ShandaMir2\\AniTiles1");
        //wemade mir3 (allowed from 200-299)
        std::vector<std::string> Mapstate = {"", "wood\\", "sand\\", "snow\\", "forest\\"};
        for (int i = 0; i < Mapstate.size(); i++)
        {
            MapLibs[200 + (i * 15)] = new MLibrary(Settings::DataPath + "Map\\WemadeMir3\\" + Mapstate[i] + "Tilesc");
            MapLibs[201 + (i * 15)] = new MLibrary(Settings::DataPath + "Map\\WemadeMir3\\" + Mapstate[i] + "Tiles30c");
            MapLibs[202 + (i * 15)] = new MLibrary(Settings::DataPath + "Map\\WemadeMir3\\" + Mapstate[i] + "Tiles5c");
            MapLibs[203 + (i * 15)] = new MLibrary(Settings::DataPath + "Map\\WemadeMir3\\" + Mapstate[i] + "Smtilesc");
            MapLibs[204 + (i * 15)] = new MLibrary(Settings::DataPath + "Map\\WemadeMir3\\" + Mapstate[i] + "Housesc");
            MapLibs[205 + (i * 15)] = new MLibrary(Settings::DataPath + "Map\\WemadeMir3\\" + Mapstate[i] + "Cliffsc");
            MapLibs[206 + (i * 15)] = new MLibrary(Settings::DataPath + "Map\\WemadeMir3\\" + Mapstate[i] + "Dungeonsc");
            MapLibs[207 + (i * 15)] = new MLibrary(Settings::DataPath + "Map\\WemadeMir3\\" + Mapstate[i] + "Innersc");
            MapLibs[208 + (i * 15)] = new MLibrary(Settings::DataPath + "Map\\WemadeMir3\\" + Mapstate[i] + "Furnituresc");
            MapLibs[209 + (i * 15)] = new MLibrary(Settings::DataPath + "Map\\WemadeMir3\\" + Mapstate[i] + "Wallsc");
            MapLibs[210 + (i * 15)] = new MLibrary(Settings::DataPath + "Map\\WemadeMir3\\" + Mapstate[i] + "smObjectsc");
            MapLibs[211 + (i * 15)] = new MLibrary(Settings::DataPath + "Map\\WemadeMir3\\" + Mapstate[i] + "Animationsc");
            MapLibs[212 + (i * 15)] = new MLibrary(Settings::DataPath + "Map\\WemadeMir3\\" + Mapstate[i] + "Object1c");
            MapLibs[213 + (i * 15)] = new MLibrary(Settings::DataPath + "Map\\WemadeMir3\\" + Mapstate[i] + "Object2c");
        }
        Mapstate = {"", "wood", "sand", "snow", "forest"};
        //shanda mir3 (allowed from 300-399)
        for (int i = 0; i < Mapstate.size(); i++)
        {
            MapLibs[300 + (i * 15)] = new MLibrary(Settings::DataPath + "Map\\ShandaMir3\\" + "Tilesc" + Mapstate[i]);
            MapLibs[301 + (i * 15)] = new MLibrary(Settings::DataPath + "Map\\ShandaMir3\\" + "Tiles30c" + Mapstate[i]);
            MapLibs[302 + (i * 15)] = new MLibrary(Settings::DataPath + "Map\\ShandaMir3\\" + "Tiles5c" + Mapstate[i]);
            MapLibs[303 + (i * 15)] = new MLibrary(Settings::DataPath + "Map\\ShandaMir3\\" + "Smtilesc" + Mapstate[i]);
            MapLibs[304 + (i * 15)] = new MLibrary(Settings::DataPath + "Map\\ShandaMir3\\" + "Housesc" + Mapstate[i]);
            MapLibs[305 + (i * 15)] = new MLibrary(Settings::DataPath + "Map\\ShandaMir3\\" + "Cliffsc" + Mapstate[i]);
            MapLibs[306 + (i * 15)] = new MLibrary(Settings::DataPath + "Map\\ShandaMir3\\" + "Dungeonsc" + Mapstate[i]);
            MapLibs[307 + (i * 15)] = new MLibrary(Settings::DataPath + "Map\\ShandaMir3\\" + "Innersc" + Mapstate[i]);
            MapLibs[308 + (i * 15)] = new MLibrary(Settings::DataPath + "Map\\ShandaMir3\\" + "Furnituresc" + Mapstate[i]);
            MapLibs[309 + (i * 15)] = new MLibrary(Settings::DataPath + "Map\\ShandaMir3\\" + "Wallsc" + Mapstate[i]);
            MapLibs[310 + (i * 15)] = new MLibrary(Settings::DataPath + "Map\\ShandaMir3\\" + "smObjectsc" + Mapstate[i]);
            MapLibs[311 + (i * 15)] = new MLibrary(Settings::DataPath + "Map\\ShandaMir3\\" + "Animationsc" + Mapstate[i]);
            MapLibs[312 + (i * 15)] = new MLibrary(Settings::DataPath + "Map\\ShandaMir3\\" + "Object1c" + Mapstate[i]);
            MapLibs[313 + (i * 15)] = new MLibrary(Settings::DataPath + "Map\\ShandaMir3\\" + "Object2c" + Mapstate[i]);
        }
        #pragma endregion
        
        LoadLibraries();
        
        Thread *thread = new Thread([&] () {LoadGameLibraries();});
        thread->IsBackground = true;
        thread->Start();

        delete thread;
    }

Libraries::StaticConstructor Libraries::staticConstructor;

    void Libraries::InitLibrary(std::vector<MLibrary*> &library, const std::string &path, const std::string &toStringValue, const std::string &suffix)
    {
        auto allFiles = Directory::GetFiles(path, "*" + suffix + MLibrary::Extention, SearchOption::TopDirectoryOnly).OrderBy([&] (std::any x)
        {
            return std::stoi(Regex::Match(x, R"(\d+)")->Value);
        });

        auto lastFile = allFiles->Count() > 0 ? FileSystem::getFileName(allFiles->Last()) : "0";

        auto count = std::stoi(Regex::Match(lastFile, R"(\d+)")->Value) + 1;

        library = std::vector<MLibrary*>(count);

        for (int i = 0; i < count; i++)
        {
//C# TO C++ CONVERTER TODO TASK: There is no C++ equivalent to 'ToString':
            library[i] = new MLibrary(path + i.ToString(toStringValue) + suffix);
        }
    }

    void Libraries::LoadLibraries()
    {
        ChrSel->Initialize();
        Progress++;

        Prguse->Initialize();
        Progress++;

        Prguse2->Initialize();
        Progress++;

        Prguse3->Initialize();
        Progress++;

        Title->Initialize();
        Progress++;
    }

    void Libraries::LoadGameLibraries()
    {
        Count = MapLibs.size() + Monsters.size() + Gates.size() + NPCs.size() + CArmours.size() + CHair.size() + CWeapons.size() + CWeaponEffect.size() + AArmours.size() + AHair.size() + AWeaponsL.size() + AWeaponsR.size() + ARArmours.size() + ARHair.size() + ARWeapons.size() + ARWeaponsS.size() + CHumEffect.size() + AHumEffect.size() + ARHumEffect.size() + Mounts.size() + Fishing.size() + Pets.size() + Transform.size() + TransformMounts.size() + TransformEffect.size() + TransformWeaponEffect.size() + 17;

        Dragon->Initialize();
        Progress++;

        BuffIcon->Initialize();
        Progress++;

        Help->Initialize();
        Progress++;

        MiniMap->Initialize();
        Progress++;

        MagIcon->Initialize();
        Progress++;
        MagIcon2->Initialize();
        Progress++;

        Magic->Initialize();
        Progress++;
        Magic2->Initialize();
        Progress++;
        Magic3->Initialize();
        Progress++;
        MagicC->Initialize();
        Progress++;

        Effect->Initialize();
        Progress++;

        GuildSkill->Initialize();
        Progress++;

        Background->Initialize();
        Progress++;

        Deco->Initialize();
        Progress++;

        Items->Initialize();
        Progress++;
        StateItems->Initialize();
        Progress++;
        FloorItems->Initialize();
        Progress++;

        for (int i = 0; i < MapLibs.size(); i++)
        {
            if (MapLibs[i] == nullptr)
            {
                MapLibs[i] = new MLibrary("");
            }
            else
            {
                MapLibs[i]->Initialize();
            }
            Progress++;
        }

        for (int i = 0; i < Monsters.size(); i++)
        {
            Monsters[i]->Initialize();
            Progress++;
        }

        for (int i = 0; i < Gates.size(); i++)
        {
            Gates[i]->Initialize();
            Progress++;
        }

        for (int i = 0; i < NPCs.size(); i++)
        {
            NPCs[i]->Initialize();
            Progress++;
        }


        for (int i = 0; i < CArmours.size(); i++)
        {
            CArmours[i]->Initialize();
            Progress++;
        }

        for (int i = 0; i < CHair.size(); i++)
        {
            CHair[i]->Initialize();
            Progress++;
        }

        for (int i = 0; i < CWeapons.size(); i++)
        {
            CWeapons[i]->Initialize();
            Progress++;
        }

        for (int i = 0; i < CWeaponEffect.size(); i++)
        {
            CWeaponEffect[i]->Initialize();
            Progress++;
        }

        for (int i = 0; i < AArmours.size(); i++)
        {
            AArmours[i]->Initialize();
            Progress++;
        }

        for (int i = 0; i < AHair.size(); i++)
        {
            AHair[i]->Initialize();
            Progress++;
        }

        for (int i = 0; i < AWeaponsL.size(); i++)
        {
            AWeaponsL[i]->Initialize();
            Progress++;
        }

        for (int i = 0; i < AWeaponsR.size(); i++)
        {
            AWeaponsR[i]->Initialize();
            Progress++;
        }

        for (int i = 0; i < ARArmours.size(); i++)
        {
            ARArmours[i]->Initialize();
            Progress++;
        }

        for (int i = 0; i < ARHair.size(); i++)
        {
            ARHair[i]->Initialize();
            Progress++;
        }

        for (int i = 0; i < ARWeapons.size(); i++)
        {
            ARWeapons[i]->Initialize();
            Progress++;
        }

        for (int i = 0; i < ARWeaponsS.size(); i++)
        {
            ARWeaponsS[i]->Initialize();
            Progress++;
        }

        for (int i = 0; i < CHumEffect.size(); i++)
        {
            CHumEffect[i]->Initialize();
            Progress++;
        }

        for (int i = 0; i < AHumEffect.size(); i++)
        {
            AHumEffect[i]->Initialize();
            Progress++;
        }

        for (int i = 0; i < ARHumEffect.size(); i++)
        {
            ARHumEffect[i]->Initialize();
            Progress++;
        }

        for (int i = 0; i < Mounts.size(); i++)
        {
            Mounts[i]->Initialize();
            Progress++;
        }


        for (int i = 0; i < Fishing.size(); i++)
        {
            Fishing[i]->Initialize();
            Progress++;
        }

        for (int i = 0; i < Pets.size(); i++)
        {
            Pets[i]->Initialize();
            Progress++;
        }

        for (int i = 0; i < Transform.size(); i++)
        {
            Transform[i]->Initialize();
            Progress++;
        }

        for (int i = 0; i < TransformEffect.size(); i++)
        {
            TransformEffect[i]->Initialize();
            Progress++;
        }

        for (int i = 0; i < TransformWeaponEffect.size(); i++)
        {
            TransformWeaponEffect[i]->Initialize();
            Progress++;
        }

        for (int i = 0; i < TransformMounts.size(); i++)
        {
            TransformMounts[i]->Initialize();
            Progress++;
        }

        Loaded = true;
    }

const std::string MLibrary::Extention = ".Lib";

    FrameSet *MLibrary::getFrames() const
    {
        return _frames;
    }

    MLibrary::MLibrary(const std::string &filename) : _fileName(Path::ChangeExtension(filename, Extention))
    {
    }

    void MLibrary::Initialize()
    {
        _initialized = true;

        if (!FileSystem::fileExists(_fileName))
        {
            return;
        }

        try
        {
            _fStream = new FileStream(_fileName, FileMode::Open, FileAccess::Read);
            _reader = new BinaryReader(_fStream);
            int currentVersion = _reader->ReadInt32();
            if (currentVersion < 2)
            {
                System::Windows::Forms::MessageBox::Show("Wrong version, expecting lib version: " + std::to_string(LibVersion) + " found version: " + std::to_string(currentVersion) + ".", _fileName, System::Windows::Forms::MessageBoxButtons::OK, System::Windows::Forms::MessageBoxIcon::Error, System::Windows::Forms::MessageBoxDefaultButton::Button1);
                System::Windows::Forms::Application::Exit();
                return;
            }
            _count = _reader->ReadInt32();

            int frameSeek = 0;
            if (currentVersion >= 3)
            {
                frameSeek = _reader->ReadInt32();
            }

            _images = std::vector<MImage*>(_count);
            _indexList = std::vector<int>(_count);

            for (int i = 0; i < _count; i++)
            {
                _indexList[i] = _reader->ReadInt32();
            }

            if (currentVersion >= 3)
            {
                _fStream->Seek(frameSeek, SeekOrigin::Begin);

                auto frameCount = _reader->ReadInt32();

                if (frameCount > 0)
                {
                    _frames = new FrameSet();
                    for (int i = 0; i < frameCount; i++)
                    {
                        Frame tempVar(_reader);
                        _frames->emplace(static_cast<MirAction>(_reader->ReadByte()), &tempVar);
                    }
                }
            }
        }
        catch (const std::runtime_error &e1)
        {
            _initialized = false;
            throw;
        }
    }

    bool MLibrary::CheckImage(int index)
    {
        if (!_initialized)
        {
            Initialize();
        }

        if (_images.empty() || index < 0 || index >= _images.size())
        {
            return false;
        }

        if (_images[index] == nullptr)
        {
            _fStream->Position = _indexList[index];
            _images[index] = new MImage(_reader);
        }
        MImage *mi = _images[index];
        if (!mi->TextureValid)
        {
            if ((mi->Width == 0) || (mi->Height == 0))
            {
                return false;
            }
            _fStream->Seek(_indexList[index] + 17, SeekOrigin::Begin);
            mi->CreateTexture(_reader);
        }

        return true;
    }

    Point *MLibrary::GetOffSet(int index)
    {
        if (!_initialized)
        {
            Initialize();
        }

        if (_images.empty() || index < 0 || index >= _images.size())
        {
            return Point::Empty;
        }

        if (_images[index] == nullptr)
        {
            _fStream->Seek(_indexList[index], SeekOrigin::Begin);
            _images[index] = new MImage(_reader);
        }

        return new Point(_images[index]->X, _images[index]->Y);
    }

    Size *MLibrary::GetSize(int index)
    {
        if (!_initialized)
        {
            Initialize();
        }
        if (_images.empty() || index < 0 || index >= _images.size())
        {
            return Size::Empty;
        }

        if (_images[index] == nullptr)
        {
            _fStream->Seek(_indexList[index], SeekOrigin::Begin);
            _images[index] = new MImage(_reader);
        }

        return new Size(_images[index]->Width, _images[index]->Height);
    }

    Size *MLibrary::GetTrueSize(int index)
    {
        if (!_initialized)
        {
            Initialize();
        }

        if (_images.empty() || index < 0 || index >= _images.size())
        {
            return Size::Empty;
        }

        if (_images[index] == nullptr)
        {
            _fStream->Position = _indexList[index];
            _images[index] = new MImage(_reader);
        }
        MImage *mi = _images[index];
        if (mi->TrueSize->IsEmpty)
        {
            if (!mi->TextureValid)
            {
                if ((mi->Width == 0) || (mi->Height == 0))
                {
                    return Size::Empty;
                }

                _fStream->Seek(_indexList[index] + 17, SeekOrigin::Begin);
                mi->CreateTexture(_reader);
            }
            return mi->GetTrueSize();
        }
        return mi->TrueSize;
    }

    void MLibrary::Draw(int index, int x, int y)
    {
        if (x >= Settings::ScreenWidth || y >= Settings::ScreenHeight)
        {
            return;
        }

        if (!CheckImage(index))
        {
            return;
        }

        MImage *mi = _images[index];

        if (x + mi->Width < 0 || y + mi->Height < 0)
        {
            return;
        }


        Rectangle tempVar(0, 0, mi->Width, mi->Height);
        Vector3 tempVar2(static_cast<float>(x), static_cast<float>(y), 0.0F);
        DXManager::Sprite->Draw(mi->Image, &tempVar, Vector3::Zero, &tempVar2, Color::White);
        mi->CleanTime = CMain::Time + Settings::CleanDelay;
    }

    void MLibrary::Draw(int index, Point *point, Color *colour, bool offSet)
    {
        if (!CheckImage(index))
        {
            return;
        }

        MImage *mi = _images[index];

        if (offSet)
        {
            point->Offset(mi->X, mi->Y);
        }

        if (point->X >= Settings::ScreenWidth || point->Y >= Settings::ScreenHeight || point->X + mi->Width < 0 || point->Y + mi->Height < 0)
        {
            return;
        }

        Rectangle tempVar(0, 0, mi->Width, mi->Height);
        Vector3 tempVar2(static_cast<float>(point->X), static_cast<float>(point->Y), 0.0F);
        DXManager::Sprite->Draw(mi->Image, &tempVar, Vector3::Zero, &tempVar2, colour);

        mi->CleanTime = CMain::Time + Settings::CleanDelay;
    }

    void MLibrary::Draw(int index, Point *point, Color *colour, bool offSet, float opacity)
    {
        if (!CheckImage(index))
        {
            return;
        }

        MImage *mi = _images[index];

        if (offSet)
        {
            point->Offset(mi->X, mi->Y);
        }

        if (point->X >= Settings::ScreenWidth || point->Y >= Settings::ScreenHeight || point->X + mi->Width < 0 || point->Y + mi->Height < 0)
        {
            return;
        }

        float oldOpacity = DXManager::Opacity;
        DXManager::SetOpacity(opacity);

        Rectangle tempVar(0, 0, mi->Width, mi->Height);
        Vector3 tempVar2(static_cast<float>(point->X), static_cast<float>(point->Y), 0.0F);
        DXManager::Sprite->Draw(mi->Image, &tempVar, Vector3::Zero, &tempVar2, colour);

        DXManager::SetOpacity(oldOpacity);
        mi->CleanTime = CMain::Time + Settings::CleanDelay;
    }

    void MLibrary::DrawBlend(int index, Point *point, Color *colour, bool offSet, float rate)
    {
        if (!CheckImage(index))
        {
            return;
        }

        MImage *mi = _images[index];

        if (offSet)
        {
            point->Offset(mi->X, mi->Y);
        }

        if (point->X >= Settings::ScreenWidth || point->Y >= Settings::ScreenHeight || point->X + mi->Width < 0 || point->Y + mi->Height < 0)
        {
            return;
        }

        bool oldBlend = DXManager::Blending;
        DXManager::SetBlend(true, rate);

        Rectangle tempVar(0, 0, mi->Width, mi->Height);
        Vector3 tempVar2(static_cast<float>(point->X), static_cast<float>(point->Y), 0.0F);
        DXManager::Sprite->Draw(mi->Image, &tempVar, Vector3::Zero, &tempVar2, colour);

        DXManager::SetBlend(oldBlend);
        mi->CleanTime = CMain::Time + Settings::CleanDelay;
    }

    void MLibrary::Draw(int index, Rectangle *section, Point *point, Color *colour, bool offSet)
    {
        if (!CheckImage(index))
        {
            return;
        }

        MImage *mi = _images[index];

        if (offSet)
        {
            point->Offset(mi->X, mi->Y);
        }


        if (point->X >= Settings::ScreenWidth || point->Y >= Settings::ScreenHeight || point->X + mi->Width < 0 || point->Y + mi->Height < 0)
        {
            return;
        }

        if (section->Right > mi->Width)
        {
            section->Width -= section->Right - mi->Width;
        }

        if (section->Bottom > mi->Height)
        {
            section->Height -= section->Bottom - mi->Height;
        }

        Vector3 tempVar(static_cast<float>(point->X), static_cast<float>(point->Y), 0.0F);
        DXManager::Sprite->Draw(mi->Image, section, Vector3::Zero, &tempVar, colour);

        mi->CleanTime = CMain::Time + Settings::CleanDelay;
    }

    void MLibrary::Draw(int index, Rectangle *section, Point *point, Color *colour, float opacity)
    {
        if (!CheckImage(index))
        {
            return;
        }

        MImage *mi = _images[index];


        if (point->X >= Settings::ScreenWidth || point->Y >= Settings::ScreenHeight || point->X + mi->Width < 0 || point->Y + mi->Height < 0)
        {
            return;
        }

        if (section->Right > mi->Width)
        {
            section->Width -= section->Right - mi->Width;
        }

        if (section->Bottom > mi->Height)
        {
            section->Height -= section->Bottom - mi->Height;
        }

        float oldOpacity = DXManager::Opacity;
        DXManager::SetOpacity(opacity);

        Vector3 tempVar(static_cast<float>(point->X), static_cast<float>(point->Y), 0.0F);
        DXManager::Sprite->Draw(mi->Image, section, Vector3::Zero, &tempVar, colour);

        DXManager::SetOpacity(oldOpacity);
        mi->CleanTime = CMain::Time + Settings::CleanDelay;
    }

    void MLibrary::Draw(int index, Point *point, Size *size, Color *colour)
    {
        if (!CheckImage(index))
        {
            return;
        }

        MImage *mi = _images[index];

        if (point->X >= Settings::ScreenWidth || point->Y >= Settings::ScreenHeight || point->X + size->Width < 0 || point->Y + size->Height < 0)
        {
            return;
        }

        float scaleX = static_cast<float>(size->Width) / mi->Width;
        float scaleY = static_cast<float>(size->Height) / mi->Height;

        Matrix *matrix = Matrix::Scaling(scaleX, scaleY, 0);
        DXManager::Sprite->Transform = matrix;
        Rectangle tempVar(0, 0, mi->Width, mi->Height);
        Vector3 tempVar2(static_cast<float>(point->X) / scaleX, static_cast<float>(point->Y) / scaleY, 0.0F);
        DXManager::Sprite->Draw(mi->Image, &tempVar, Vector3::Zero, &tempVar2, Color::White);
        DXManager::Sprite->Transform = Matrix::Identity;

        mi->CleanTime = CMain::Time + Settings::CleanDelay;
    }

    void MLibrary::DrawTinted(int index, Point *point, Color *colour, Color *Tint, bool offSet)
    {
        if (!CheckImage(index))
        {
            return;
        }

        MImage *mi = _images[index];

        if (offSet)
        {
            point->Offset(mi->X, mi->Y);
        }

        if (point->X >= Settings::ScreenWidth || point->Y >= Settings::ScreenHeight || point->X + mi->Width < 0 || point->Y + mi->Height < 0)
        {
            return;
        }

        Rectangle tempVar(0, 0, mi->Width, mi->Height);
        Vector3 tempVar2(static_cast<float>(point->X), static_cast<float>(point->Y), 0.0F);
        DXManager::Sprite->Draw(mi->Image, &tempVar, Vector3::Zero, &tempVar2, colour);

        if (mi->HasMask)
        {
            Rectangle tempVar3(0, 0, mi->Width, mi->Height);
            Vector3 tempVar4(static_cast<float>(point->X), static_cast<float>(point->Y), 0.0F);
            DXManager::Sprite->Draw(mi->MaskImage, &tempVar3, Vector3::Zero, &tempVar4, Tint);
        }

        mi->CleanTime = CMain::Time + Settings::CleanDelay;
    }

    void MLibrary::DrawUp(int index, int x, int y)
    {
        if (x >= Settings::ScreenWidth)
        {
            return;
        }

        if (!CheckImage(index))
        {
            return;
        }

        MImage *mi = _images[index];
        y -= mi->Height;
        if (y >= Settings::ScreenHeight)
        {
            return;
        }
        if (x + mi->Width < 0 || y + mi->Height < 0)
        {
            return;
        }

        Rectangle tempVar(0, 0, mi->Width, mi->Height);
        Vector3 tempVar2(x, y, 0.0F);
        DXManager::Sprite->Draw(mi->Image, &tempVar, Vector3::Zero, &tempVar2, Color::White);
        mi->CleanTime = CMain::Time + Settings::CleanDelay;
    }

    void MLibrary::DrawUpBlend(int index, Point *point)
    {
        if (!CheckImage(index))
        {
            return;
        }

        MImage *mi = _images[index];

        point->Y -= mi->Height;


        if (point->X >= Settings::ScreenWidth || point->Y >= Settings::ScreenHeight || point->X + mi->Width < 0 || point->Y + mi->Height < 0)
        {
            return;
        }

        bool oldBlend = DXManager::Blending;
        DXManager::SetBlend(true, 1);

        Rectangle tempVar(0, 0, mi->Width, mi->Height);
        Vector3 tempVar2(static_cast<float>(point->X), static_cast<float>(point->Y), 0.0F);
        DXManager::Sprite->Draw(mi->Image, &tempVar, Vector3::Zero, &tempVar2, Color::White);

        DXManager::SetBlend(oldBlend);
        mi->CleanTime = CMain::Time + Settings::CleanDelay;
    }

    bool MLibrary::VisiblePixel(int index, Point *point, bool accuate)
    {
        if (!CheckImage(index))
        {
            return false;
        }

        if (accuate)
        {
            return _images[index]->VisiblePixel(point);
        }

        int accuracy = 2;

        for (int x = -accuracy; x <= accuracy; x++)
        {
            for (int y = -accuracy; y <= accuracy; y++)
            {
                Point tempVar(point->X + x, point->Y + y);
                if (_images[index]->VisiblePixel(&tempVar))
                {
                    return true;
                }
            }
        }

        return false;
    }

    MImage::MImage(BinaryReader *reader)
    {
        //read layer 1
        Width = reader->ReadInt16();
        Height = reader->ReadInt16();
        X = reader->ReadInt16();
        Y = reader->ReadInt16();
        ShadowX = reader->ReadInt16();
        ShadowY = reader->ReadInt16();
        Shadow = reader->ReadByte();
        Length = reader->ReadInt32();

        //check if there's a second layer and read it
        HasMask = ((Shadow >> 7) == 1) ? true : false;
        if (HasMask)
        {
            reader->ReadBytes(Length);
            MaskWidth = reader->ReadInt16();
            MaskHeight = reader->ReadInt16();
            MaskX = reader->ReadInt16();
            MaskY = reader->ReadInt16();
            MaskLength = reader->ReadInt32();
        }
    }

    Size *MImage::GetTrueSize()
    {
        if (TrueSize != Size::Empty)
        {
            return TrueSize;
        }

        int l = 0, t = 0, r = Width, b = Height;

        bool visible = false;
        for (int x = 0; x < r; x++)
        {
            for (int y = 0; y < b; y++)
            {
                Point tempVar(x, y);
                if (!VisiblePixel(&tempVar))
                {
                    continue;
                }

                visible = true;
                break;
            }

            if (!visible)
            {
                continue;
            }

            l = x;
            break;
        }

        visible = false;
        for (int y = 0; y < b; y++)
        {
            for (int x = l; x < r; x++)
            {
                Point tempVar2(x, y);
                if (!VisiblePixel(&tempVar2))
                {
                    continue;
                }

                visible = true;
                break;

            }
            if (!visible)
            {
                continue;
            }

            t = y;
            break;
        }

        visible = false;
        for (int x = r - 1; x >= l; x--)
        {
            for (int y = 0; y < b; y++)
            {
                Point tempVar3(x, y);
                if (!VisiblePixel(&tempVar3))
                {
                    continue;
                }

                visible = true;
                break;
            }

            if (!visible)
            {
                continue;
            }

            r = x + 1;
            break;
        }

        visible = false;
        for (int y = b - 1; y >= t; y--)
        {
            for (int x = l; x < r; x++)
            {
                Point tempVar4(x, y);
                if (!VisiblePixel(&tempVar4))
                {
                    continue;
                }

                visible = true;
                break;

            }
            if (!visible)
            {
                continue;
            }

            b = y + 1;
            break;
        }

        TrueSize = Rectangle::FromLTRB(l, t, r, b).Size;

        return TrueSize;
    }

    std::vector<unsigned char> MImage::DecompressImage(std::vector<unsigned char> &image)
    {
//C# TO C++ CONVERTER NOTE: The following 'using' block is replaced by its C++ equivalent:
//ORIGINAL LINE: using (GZipStream stream = new GZipStream(new MemoryStream(image), CompressionMode.Decompress))
        {
            MemoryStream tempVar(image);
            GZipStream stream = GZipStream(&tempVar, CompressionMode::Decompress);
            constexpr int size = 4096;
            std::vector<unsigned char> buffer(size);
//C# TO C++ CONVERTER NOTE: The following 'using' block is replaced by its C++ equivalent:
//ORIGINAL LINE: using (MemoryStream memory = new MemoryStream())
            {
                MemoryStream memory = MemoryStream();
                int count = 0;
                do
                {
                    count = stream.Read(buffer, 0, size);
                    if (count > 0)
                    {
                        memory.Write(buffer, 0, count);
                    }
                } while (count > 0);
                return memory.ToArray();
            }
        }
    }
}
